package com.cloudinnov.utils;

import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cloudinnov.logic.CarDetectorLogic;  
import com.cloudinnov.logic.impl.GlobalInitLogicImpl;
import com.cloudinnov.model.CarDetectorData;
import com.cloudinnov.utils.support.spring.SpringUtils;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

/**
 * Description: <br/>
 * Copyright (c),2011-2017 <br/>
 * This program is protected by copyright Cloudinnov; <br/>
 * Program Name: <br/>
 * Dte:
 * @author chengning
 * @date 2017年3月17日下午12:57:41
 * @email ningcheng@cloudinnov.com
 * @remark
 * @version
 */
public class MultiThreadServer {
	static final Logger LOG = LoggerFactory.getLogger(MultiThreadServer.class);
	private ServerSocket serverSocket;
	private ThreadPoolExecutor executor;
	static CarDetectorLogic carDetectorLogic = SpringUtils.getBean("carDetectorLogic");
	static JedisPool jedisPool = SpringUtils.getBean("jedisPool");
	private int port;
	private Socket socket;
	private boolean serverStatus;
	
	public MultiThreadServer(int port) throws IOException {
		serverSocket = new ServerSocket(port);
		this.port = port;
		executor = new ThreadPoolExecutor(50, 100, 1L, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100));
		LOG.debug("车检仪服务器即将启动，等待客户端的连接,port: " + port);
		GlobalInitLogicImpl.CAR_TCP_START_UP = true;//标识车检仪启动成功
		serverStatus = true;
	}
	public void service() {
		while (true) {
		    //判断Server是否关闭
            if(serverSocket.isClosed() || serverSocket == null) {
                while(!serverStatus) {
                    try {
                        serverSocket = new ServerSocket(port);
                        serverStatus = true;
                    } catch (IOException e) {
                        LOG.error("restart CarTcpServer fail.");
                        serverStatus = false;
                        serverSocket = null;
                    }
                }
            }
			try {
				socket = serverSocket.accept();
				executor.execute(new Handler(socket, "车检仪数据处理线程" + socket.toString()));
			} catch (IOException e) {
				LOG.error("getSocket is bad, socket : {}, error: {}", socket, e);
				serverStatus = false;
				try {
                    serverSocket.close();
                    serverSocket = null;
                } catch (IOException e1) {
                    LOG.error("由于异常stop CarTcpServer is fail, socket : {}, error: {}", socket, e);
                }
			}
		}
	}
	public static void main(String[] args) throws IOException {
		new MultiThreadServer(8800).service();
	}
}

class Handler implements Runnable {
	static final Logger LOG = LoggerFactory.getLogger(Handler.class);
	private static final int DATA_LENGTH = 178;
	public static final String TOTAL_CAR = "total:car";
	public static final String CAL_NAME = "car:deviceId:";
	public static final String CAL_VALUE = ":value";
	private static final int RECENT_LIST_COUNT = 10000;
	public static final String SPLITTER_LEVEL0 = ",";
	private final Socket socket;
	private String name;

	public Handler(Socket socket, String name) {
		this.socket = socket;
		this.name = name;
	}
	public void run() {
	
		Thread.currentThread().setName(name);
		Jedis redis = null;
		InputStream in = null;
		String requestData = null;
		try {
			redis = MultiThreadServer.jedisPool.getResource();
			// 与客户端建立通信，获取输入流，读取取客户端提供的信息
			in = socket.getInputStream();
			byte[] data = new byte[DATA_LENGTH];
			int len = in.read(data);
			requestData = bytesToHexString(data, len).trim();
			 
			LOG.debug("recive lineData, ip : {}, data : {}", socket.getRemoteSocketAddress(), requestData);
			if (CommonUtils.isNotEmpty(requestData) && requestData.length() >= 64 && requestData.length() <= 215) {
				String keyRealtime, totalName, value;
				CarDetectorData model = null;
				if (data.length > 44) {
					model = new CarDetectorData();
				}
				int lineId = 1;
				// 得到设备唯一ID
				model.setDeviceId(requestData.substring(8, 24));
				// 获取时间和日期
				model.setSummaryTime(requestData.substring(40, 54));
				// 获取周期
				model.setPeriod(Integer.parseInt(requestData.substring(54, 58), 16));
				// 车辆总数
				model.setCarTotal(Long.parseLong(requestData.substring(58, 62), 16));
				//获取前三车道后三车道的总数
				Integer beyondLaneData= Integer.parseInt(requestData.substring(68, 72),16)+Integer.parseInt(requestData.substring(92,96),16)+Integer.parseInt(requestData.substring(116,120),16);
				Integer behindLaneData=Integer.parseInt(requestData.substring(140,144),16)+Integer.parseInt(requestData.substring(164,168),16)+Integer.parseInt(requestData.substring(188,192),16);
				 
				// 车道数
				model.setLaneTotal(Integer.parseInt(requestData.substring(62, 64), 16));
				model.setUtcTime(new Date());
				model.setTimeMillis(System.currentTimeMillis());
				// 车道数据共12个字节和2字节crc校验包括：
				String lineData = requestData.substring(64, requestData.length() - 4);
				keyRealtime = CAL_NAME + model.getDeviceId() + CAL_VALUE;
				totalName = TOTAL_CAR + CAL_VALUE;
				value = model.getCarTotal() + SPLITTER_LEVEL0 + model.getTimeMillis()+SPLITTER_LEVEL0+beyondLaneData+SPLITTER_LEVEL0+behindLaneData;
				redis.lpush(keyRealtime, value);// 单个车检仪器只保存最新的3000条数据
				redis.ltrim(keyRealtime, 0L, RECENT_LIST_COUNT);
				redis.set(totalName, value);// 总数只保留最新的一条
				LOG.debug("keyRealtime:\t:" + keyRealtime + "\tvalue：\t" + value);
				List<CarDetectorData.CarDetectorDataChilren> lineDatas = new ArrayList<>();
				for (int j = 0; j < lineData.length(); j += 24) {
					if (lineData.substring(j, j + 5).indexOf("ff00") != -1) {
						break;
					}
					if (lineData.length() < j + 24) {// 判断车道数据包长度是否满足要求
						break;
					}
					CarDetectorData.CarDetectorDataChilren laneData = new CarDetectorData().new CarDetectorDataChilren();
					laneData.setLineAvgVelocuty(Integer.parseInt(lineData.substring(j, j + 2), 16));
					laneData.setLineOccRate(Integer.parseInt(lineData.substring(j + 2, j + 4), 16));
					laneData.setLineFlow(Integer.parseInt(lineData.substring(j + 4, j + 8), 16));
					laneData.setLineOneClassflow(Integer.parseInt(lineData.substring(j + 8, j + 12), 16));
					laneData.setLineTwoClassflow(Integer.parseInt(lineData.substring(j + 12, j + 16), 16));
					laneData.setLineThreeClassflow(Integer.parseInt(lineData.substring(j + 16, j + 20), 16));
					laneData.setLineFourClassflow(Integer.parseInt(lineData.substring(j + 20, j + 24), 16));
					laneData.setLineId(lineId);
					lineDatas.add(laneData);
					lineId++;
				}
				model.setData(lineDatas);
				MultiThreadServer.carDetectorLogic.saveRealTimeDataToMongoDB(model);
			}
		} catch (Exception e) {
			LOG.error("requestCarDetectorData is bad, data : {},  error: {} ", requestData, e);
		} finally {
			MultiThreadServer.jedisPool.returnResource(redis);
			try {
				if (in != null) {
					in.close();
					in = null;
				}
			} catch (Exception e) {
				LOG.error("close inputStream is bad, socket : {}, error: {}", socket.toString(), e);
			}
			try {
				if (socket != null) {
					socket.close();
				}
			} catch (Exception e) {
				LOG.error("close socket is bad, socket : {}, error: {}", socket.toString(), e);
			}
		}
	}
	/**
	 * byte数组转换成16进制字符串
	 * @param src
	 * @return
	 */
	public static String bytesToHexString(byte[] src) {
		StringBuilder stringBuilder = new StringBuilder();
		if (src == null || src.length <= 0) {
			return null;
		}
		for (int i = 0; i < src.length; i++) {
			int v = src[i] & 0xFF;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				stringBuilder.append(0);
			}
			stringBuilder.append(hv);
		}
		return stringBuilder.toString();
	}
	private String bytesToHexString(byte[] data, int length) {
		StringBuilder stringBuilder = new StringBuilder();
		if (data == null || length <= 0) {
			return null;
		}
		for (int i = 0; i < length; i++) {
			int v = data[i] & 0xFF;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				stringBuilder.append(0);
			}
			stringBuilder.append(hv);
		}
		return stringBuilder.toString();
	}
}
